package com.jhc.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jhc.bo.UserSaveDetails;
import com.jhc.dto.CmsTreePermission;
import com.jhc.dto.CmsUpdatePasswordDto;
import com.jhc.dto.CmsUserPermissionAdd;
import com.jhc.dto.CmsUserRoleAdd;
import com.jhc.entity.*;
import com.jhc.mapper.*;
import com.jhc.service.ICmsUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jhc.service.RedisService;
import com.jhc.utils.JHC;
import com.jhc.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户  服务实现类
 * </p>
 *
 * @author zhengyue
 * @since 2020-03-13
 */
@Service
@Slf4j
public class CmsUserServiceImpl extends ServiceImpl<CmsUserMapper, CmsUser> implements ICmsUserService {

    @Autowired
    private BacCollegeMapper bacCollegeMapper;

    //@Autowired
    //private CmsTeacherMapper cmsTeacherMapper;

    @Autowired
    private CmsRoleMapper cmsRoleMapper;

    @Autowired
    private CmsPermissionMapper cmsPermissionMapper;

    @Autowired
    private CmsUserMapper cmsUserMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserSaveDetails userSaveDetails;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Value("${jwt.expiration}")
    private Long redisTime;

    /**
     * 获取用户所有权限
     *
     * @param number 用户账号 职工号
     * @return 用户权限列表
     */
    @Override
    public List<CmsPermission> getPermissionList(String number) {

        // 查询用户权限值
        CmsUser cmsAdmin = cmsUserMapper.selectOne(new QueryWrapper<CmsUser>()
                .lambda()
                .eq(CmsUser::getNumber, number));
        if (cmsAdmin == null) {
            return new ArrayList<>();
        }
        try {
            // 字符串生成对象
            UserAuthData userAuthData = JSONUtil.toBean(cmsAdmin.getAuthData(), UserAuthData.class);
            // 权限列表
            List<String> authList = userAuthData.getAuthList();
            // 角色列表
            List<String> roleList = userAuthData.getRoleList();
            // 查询角色 拿到角色表中的权限值
            System.out.println(roleList.toString());
            if (roleList.size() != 0) {
                List<CmsRole> authRoleList = cmsRoleMapper.selectList(new QueryWrapper<CmsRole>().lambda()
                        .select(CmsRole::getAuthData)
                        .in(CmsRole::getId, roleList));
                // 拿到角色中的权限值并保存
                for (CmsRole cmsRole : authRoleList) {
                    UserAuthData roleAuth = JSONUtil.toBean(cmsRole.getAuthData(), UserAuthData.class);
                    if (roleAuth.getAuthList() != null) {
                        authList.addAll(roleAuth.getAuthList());
                    }
                }
            }
            // 去重
            authList = authList.stream().distinct().collect(Collectors.toList());
            // 查出权限列表
            if (authList.size() != 0) {
                return cmsPermissionMapper.selectList(
                        new QueryWrapper<CmsPermission>().lambda()
                                .in(CmsPermission::getValue, authList));
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return new ArrayList<>();
    }

    /**
     * 用户登陆
     *
     * @param number 用户名
     * @param password 密码
     * @return token
     */
    @Override
    public RedisUserInfo login(String number, String password) {
        String token = null;
        RedisUserInfo redisUserInfo = new RedisUserInfo();
        try {
            UserDetails userDetails = userDetailsService.loadUserByUsername(number);
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities()
            );
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            token = jwtTokenUtil.generateToken(userDetails);

            // 获取用户所携带的路由信息
            List<CmsPermission> cmsPermissionList = getPermissionList(number);
            List<CmsTreePermission> cmsTreePermissionList = JHC.cmsTreePermissions(cmsPermissionList);
            redisUserInfo.setStaffNumber(number);
            //查询用户信息
            CmsUser user = cmsUserMapper.selectOne(new QueryWrapper<CmsUser>().lambda().eq(CmsUser::getNumber, number));
            //查询学院信息
            BacCollege college = bacCollegeMapper.selectOne(new QueryWrapper<BacCollege>().lambda().eq(BacCollege::getNumber, user.getCollegeNumber()));
            if (college != null) {
                redisUserInfo.setCollegeName(college.getName());
            }
            redisUserInfo.setId(user.getId());
            redisUserInfo.setStaffName(user.getName());
            redisUserInfo.setPermissionList(cmsPermissionList);
            redisUserInfo.setRouter(cmsTreePermissionList);
            redisUserInfo.setToken(tokenHead + token);
            redisService.set(number, JSON.toJSONString(redisUserInfo));
            redisService.expire(number, redisTime);

        } catch (Exception e) {
            log.warn("登陆异常:{}", e.getMessage());
        }
        return redisUserInfo;
    }

    /**
     * 根据ID查找用户id和身份信息
     *
     * @param id 用户ID
     * @return 带有id和身份信息的实体对象
     */
    private CmsUser getUserAuth(Long id) {
        LambdaQueryWrapper<CmsUser> queryWrapper = new QueryWrapper<CmsUser>()
                .select("auth_data", "id").lambda()
                .eq(CmsUser::getId, id);
        return cmsUserMapper.selectOne(queryWrapper);
    }

    private void clearToken(boolean ifFast, Long id) {
        // 判断是否马上登出
        CmsUser cmsUser = cmsUserMapper.selectOne(new QueryWrapper<CmsUser>()
                .lambda()
                .select(CmsUser::getNumber)
                .eq(CmsUser::getId, id));
        if (ifFast && cmsUser != null) {
            redisService.remove(cmsUser.getNumber());
        } else if (!ifFast && cmsUser != null) {
            redisService.expire(cmsUser.getNumber(), 60 * 60);
        }
    }

    /**
     * 给用户添加权限
     *
     * @param cmsUserPermissionAdd 添加权限对象
     * @return 是否添加成功
     */
    @Override
    public boolean userAddPermission(CmsUserPermissionAdd cmsUserPermissionAdd) {
        CmsUser cmsUser = getUserAuth(cmsUserPermissionAdd.getId());
        if (cmsUser == null) {
            return false;
        }
        // 拿出原本的角色 + 权限 字符串
        String authStr = cmsUser.getAuthData();
        // 把字符串转对象
        UserAuthData userAuthData = JSONUtil.toBean(authStr, UserAuthData.class);
        // 把新的权限列表设置到对象中
        userAuthData.setAuthList(cmsUserPermissionAdd.getAuthList());
        // 把对象转化成json设置到对象中
        cmsUser.setAuthData(JSONUtil.toJsonStr(userAuthData));
        // 清除token
        if (cmsUserMapper.updateById(cmsUser) == 1) {
            clearToken(cmsUserPermissionAdd.getIfFast(), cmsUserPermissionAdd.getId());
            return true;
        }
        return false;
    }

    /**
     * 给用户添加角色
     *
     * @param cmsUserRoleAdd 添加角色对象
     * @return 是否添加成功
     */
    @Override
    public Boolean userAddRole(CmsUserRoleAdd cmsUserRoleAdd) {
        CmsUser cmsUser = getUserAuth(cmsUserRoleAdd.getId());
        if (cmsUser == null) {
            return false;
        }
        if (cmsRoleMapper.selectById(cmsUserRoleAdd.getRoleId()) == null) {
            return false;
        }
        // 拿出原本的角色 + 权限 字符串
        String authStr = cmsUser.getAuthData();
        // 把字符串转化成对象（原本的）
        UserAuthData userAuthData = JSONUtil.toBean(authStr, UserAuthData.class);
        // 把添加的角色ID放进去
        userAuthData.getRoleList().add(cmsUserRoleAdd.getRoleId());
        // 把对象转化成json存到对象
        cmsUser.setAuthData(JSONUtil.toJsonStr(userAuthData));
        // 清除token
        if (cmsUserMapper.updateById(cmsUser) == 1) {
            clearToken(cmsUserRoleAdd.getIfFast(), cmsUserRoleAdd.getId());
            return true;
        }
        return false;
    }

    /**
     * 删除角色ID
     *
     * @param cmsUserRoleAdd 删除角色对象
     * @return
     */
    @Override
    public Boolean userRemoveRole(CmsUserRoleAdd cmsUserRoleAdd) {
        CmsUser cmsUser = getUserAuth(cmsUserRoleAdd.getId());
        // 如果用户不存在
        if (cmsUser == null) {
            return false;
        }
        // 如果角色不存在
        if (cmsRoleMapper.selectById(cmsUserRoleAdd.getRoleId()) == null) {
            return false;
        }
        // 拿出原本的角色 + 权限 字符串
        String authStr = cmsUser.getAuthData();
        // 把字符串转化成对象（原本的）
        UserAuthData userAuthData = JSONUtil.toBean(authStr, UserAuthData.class);
        // 删除角色ID
        userAuthData.setRoleList(userAuthData.getRoleList().stream().filter(item -> !cmsUserRoleAdd.getRoleId().equals(item)).collect(Collectors.toList()));
        // 把对象转化成json存到对象
        cmsUser.setAuthData(JSONUtil.toJsonStr(userAuthData));
        // 清除token
        if (cmsUserMapper.updateById(cmsUser) == 1) {
            clearToken(cmsUserRoleAdd.getIfFast(), cmsUserRoleAdd.getId());
            return true;
        }
        return false;
    }

    /**
     * 密码更新
     * @param cmsUpdatePasswordDto
     * @return
     */
    @Override
    public boolean updatePassword(CmsUpdatePasswordDto cmsUpdatePasswordDto) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(cmsUpdatePasswordDto.getNumber());
        if (!passwordEncoder.matches(cmsUpdatePasswordDto.getOldPassword(), userDetails.getPassword())) {
            throw new BadCredentialsException("密码不正确");
        }
        cmsUpdatePasswordDto.setPassword(passwordEncoder.encode(cmsUpdatePasswordDto.getPassword()));
        CmsUser cmsUser = new CmsUser();
        BeanUtils.copyProperties(cmsUpdatePasswordDto, cmsUser);
        if (cmsUserMapper.update(cmsUser, new UpdateWrapper<CmsUser>().lambda()
                .eq(CmsUser::getNumber, cmsUpdatePasswordDto.getNumber())) == 1) {
            redisService.remove(cmsUser.getNumber());
            return true;
        }
        return false;
    }

    /**
     * 校验老师编号列表是否存在
     *
     * @param numbers
     * @return
     */
    @Override
    public Boolean isUserNumberList(List<String> numbers) {
        for (String userNumber : numbers) {
            CmsUser user = cmsUserMapper.selectOne(new QueryWrapper<CmsUser>().lambda().eq(CmsUser::getNumber, userNumber));
            if (user == null) {
                return false;
            }
        }
        return true;
    }

    /**
     * 根据用户姓名模糊查询
     */
    @Override
    public List<CmsUser> getUserListByLikeName(String name) {
        List<CmsUser> cmsUsers = cmsUserMapper.selectList(new QueryWrapper<CmsUser>().lambda()
                .like(CmsUser::getName, name));
        return cmsUsers;
    }
}
